package com.es.www.kyl.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.es.www.kyl.bean.UserContext;
import com.es.www.kyl.bean.req.LoginReq;
import com.es.www.kyl.bean.req.WxLoginReq;
import com.es.www.kyl.bean.req.WxRefreshTokenReq;
import com.es.www.kyl.bean.res.LoginRes;
import com.es.www.kyl.bean.res.client.WxBindDTO;
import com.es.www.kyl.bean.res.client.WxLoginRes;
import com.es.www.kyl.config.property.JwtProperty;
import com.es.www.kyl.config.property.WxConfigProperty;
import com.es.www.kyl.constant.BaseConstants;
import com.es.www.kyl.constant.BusinessResultEnum;
import com.es.www.kyl.constant.RedisConstants;
import com.es.www.kyl.domain.TokenBo;
import com.es.www.kyl.domain.User;
import com.es.www.kyl.exception.ResException;
import com.es.www.kyl.mapper.UserMapper;
import com.es.www.kyl.service.JwtService;
import com.es.www.kyl.service.LoginService;
import com.es.www.kyl.util.*;
import javafx.util.Pair;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.es.www.kyl.constant.RedisConstants.WEB_TOKEN_KEY_SUFFIX;

/**
 * @author estranger
 */
@RequiredArgsConstructor
@Service("LoginService")
public class LoginServiceImpl implements LoginService {


    private final UserMapper userMapper;
    private final JwtService jwtService;
    private final RedisUtils redisUtils;
    private final JwtProperty jwtProperty;
    private final RestTemplate restTemplate;
    private final WxConfigProperty wxConfigProperty;

    @Override
    public LoginRes refreshToken(WxRefreshTokenReq req) {
        String userId = redisUtils.getStringValue(RedisConstants.REFRESH_TOKEN_KEY_PREFIX + req.getRefreshToken());
        /**
         *  不存在此refreshToken：
         *      1. 说明用户已经很久没有操作
         *      2. 被别人截取了refresh token，自己的已经失效
         *  需要对这个用户的所有的refresh token进行清除（没做)
         */
        if (Objects.isNull(userId)) {
            throw new ResException(BusinessResultEnum.TOKEN_NOT_EXIST);
        }
        //生成token
        String newAccessToken = jwtService.generateAccessToken(Integer.valueOf(userId));
        String newRefreshToken = UUID.randomUUID().toString().replace("-", "");
        //删除旧的refreshToken
        redisUtils.deleteCache(RedisConstants.REFRESH_TOKEN_KEY_PREFIX + req.getRefreshToken());
        redisUtils.setStringValue(RedisConstants.REFRESH_TOKEN_KEY_PREFIX + newRefreshToken, userId, jwtProperty.getRefreshExpiration(), TimeUnit.MINUTES);
        return LoginRes.builder()
                .userId(Integer.valueOf(userId))
                .accessToken(newAccessToken)
                .refreshToken(newRefreshToken)
                .build();
    }

    @Override
    public void exitLogin(String accessToken) {
        jwtService.doValidateToken(accessToken);
    }

    @Override
    public LoginRes webLogin(LoginReq loginReq) {
        //验证验证码是否正确
        checkVerifyCode(loginReq.getPhone(), loginReq.getVerifyCode());
        //查询用户
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUserPhone, loginReq.getPhone()));
        if (Objects.isNull(user)) {
            throw new ResException(BusinessResultEnum.USER_NOT_EXIST);
        }else if (Objects.equals(user.getUserRole(), BaseConstants.UserRole.USER.getUserRole())) {
            throw new ResException(BusinessResultEnum.USER_PERMISSION_DENIED);
        }
        //生成token
        return LoginRes.builder()
                .userId(user.getId())
                .accessToken(createWebToken(user))
                .refreshToken(null)
                .build();
    }

    private String createWebToken(User user) {
        //生成token
        String token = IdUtil.randomUUID();
        UserContext userContext = new UserContext();
        userContext.setUserId(user.getId());
        userContext.setUserRole(user.getUserRole());
        userContext.setPerType(user.getPerType());
        userContext.setOpenId(user.getOpenId());
        //放入redis中
        redisUtils.setStringValue(WEB_TOKEN_KEY_SUFFIX + token, JSONUtil.toJsonStr(userContext), 1800L, TimeUnit.SECONDS);
        return token;
    }

    @Override
    public WxLoginRes appLogin(LoginReq loginReq) {
        //验证验证码是否正确
        checkVerifyCode(loginReq.getPhone(), loginReq.getVerifyCode());
        //查询用户
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUserPhone, loginReq.getPhone()));
        TokenBo tokenBo = generateToken(user, null);
        return new WxLoginRes(user.getId(),
                null,
                tokenBo.getAccessToken(),
                tokenBo.getRefreshToken(),
                user.getUserRole(),
                user.getPerType(),
                user.getAvatarUrl(),
                user.getName(),
                user.getUserPhone()
        );
    }

    @Override
    public WxLoginRes wxLogin(WxLoginReq loginReq) {
        //调用微信接口获取openId
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + wxConfigProperty.getAppId() +
                "&secret=" + wxConfigProperty.getSecret() +
                "&js_code=" + loginReq.getJsCode() +
                "&grant_type=authorization_code";
        ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
        if (response.getStatusCode().isError() || StringUtils.isEmpty(response.getBody())) {
            throw new ResException(BusinessResultEnum.WX_BIND_ERROR);
        }

        WxBindDTO wxBindDto = JSON.parseObject(response.getBody(), WxBindDTO.class);
        if (StringUtils.isNotEmpty(wxBindDto.getErrcode())) {
            throw new ResException(BusinessResultEnum.WX_BIND_ERROR.getCode(), wxBindDto.getErrmsg());
        }
        String wxOpenId = wxBindDto.getOpenid();
        //查询用户
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getOpenId, wxOpenId));
        TokenBo tokenBo = generateToken(user, wxOpenId);
        return new WxLoginRes(user.getId(),
                wxOpenId,
                tokenBo.getAccessToken(),
                tokenBo.getRefreshToken(),
                user.getUserRole(),
                user.getPerType(),
                user.getAvatarUrl(),
                user.getName(),
                user.getUserPhone()
        );
    }

    private TokenBo generateToken(User user, String wxOpenId) {
        if (Objects.isNull(user)) {
            // 新用户，注册
            user = new User();
            //随机生成一个name
            user.setName(IdUtil.fastSimpleUUID());
            user.setCreateTime(new Date());
            user.setCreateBy(0);
            if (StringUtils.isNotBlank(wxOpenId)) {
                user.setOpenId(wxOpenId);
            }
            userMapper.insertSelective(user);
        }
        //生成token
        String accessToken = jwtService.generateAccessToken(user.getId());
        String refreshToken = UUID.randomUUID().toString().replace("-", "");
        //将refreshToken存入redis
        redisUtils.setStringValue(RedisConstants.REFRESH_TOKEN_KEY_PREFIX + refreshToken, String.valueOf(user.getId()), jwtProperty.getRefreshExpiration(), TimeUnit.MINUTES);
        return new TokenBo(accessToken, refreshToken, user);
    }


    private void checkVerifyCode(String userPhone, String verifyCodeStr) {
        String verifyCode = redisUtils.getStringValue(RedisConstants.SMS_VERIFY_CODE_KEY_PREFIX + userPhone);
        if (!Objects.equals(verifyCodeStr, verifyCode)) {
            throw new ResException(BusinessResultEnum.LOGIN_SMS_CODE_ERROR);
        }
    }
}
